/**********************************************************************\
 © COPYRIGHT 2006 Corporation for National Research Initiatives (CNRI);
                        All rights reserved.
               
        The HANDLE.NET software is made available subject to the
         Handle System Public License, which may be obtained at
         http://hdl.handle.net/4263537/5030 or hdl:4263537/5030
\**********************************************************************/

package net.handle.apps.servlet_proxy;

import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.URLEncoder;
import java.lang.reflect.*;
import java.net.Socket;

import net.handle.hdllib.*;
import net.handle.util.StringUtils;

public class AccessLog {
  private static String SINGLETON_LOCK = "<access log singleton lock>";
  private static AccessLog singleton = null;
  private Properties config;
  private Writer log = null;
  private boolean logHsAdmin = false;
  private boolean logReferrer = false;
  private int accessLogBufsiz = 512;

  private int flushCounter = 0;
  private long lastFlushTime = 0;
  
  // access log

  AccessLog(Properties config) {
    logReferrer = Boolean.valueOf(config.getProperty("log_referrer","false")).booleanValue();
    logHsAdmin = Boolean.valueOf(config.getProperty("log_hs_admin","false")).booleanValue();
    try {
      accessLogBufsiz = Integer.parseInt(config.getProperty("access_log_bufsiz", "2048"));
    } catch (NumberFormatException e) {
      e.printStackTrace();
    }
    
    String logfile = config.getProperty("access_log");
    System.err.println("Using log file: '"+logfile+"'"); 
    if (logfile != null){
      try {
        if(accessLogBufsiz<=0)
          log = new FileWriter(logfile, true);
        else
          log = new BufferedWriter(new FileWriter(logfile, true), accessLogBufsiz);
      } catch (IOException e){
        System.err.println("Error opening log: "+e); 
      }
    }

    System.err.println("access log started at '"+logfile+"'");
  }

  /**
   * Return the singleton AccessLog object.  If the AccessLog singleton does not
   * already exist, construct one with the given configuration settings.
   */
  public static final AccessLog getLogger(Properties config) {
    if(singleton!=null) return singleton;
    synchronized (SINGLETON_LOCK) {
      if(singleton==null) {
        System.gc();
        System.runFinalization();
        singleton = new AccessLog(config);
      }
    }
    return singleton;
  }
  
  public synchronized void logAccess(String accessType, int oc, int rc,
                                     String addr, String hdl,
                                     HandleValue vals[], String referrer, long time)
  {
    if (log == null) return;
    StringBuffer msg = new StringBuffer(50);
    msg.append(addr==null ? "" : addr);
    msg.append(' ');
    msg.append(accessType);
    msg.append(" \"");
    msg.append(new Date());
    msg.append("\" ");
    msg.append(oc);
    msg.append(' ');
    msg.append(rc);
    msg.append(' ');
    msg.append(time);
    msg.append("ms ");
    msg.append(hdl);
    
    if (logHsAdmin){
      msg.append(" \"");
      boolean firstAdmin = true;
      for (int i=0; vals!=null && i<vals.length; i++){
        if (!vals[i].getTypeAsString().equals("HS_ADMIN")){
          continue;
        }
        AdminRecord adm = new AdminRecord();
        try {
          Encoder.decodeAdminRecord(vals[i].getData(), 0, adm);
        }
        catch (HandleException e){
          continue;
        }
        if (!firstAdmin) msg.append(',');
        firstAdmin = false;
        
        msg.append(adm.adminIdIndex);
        msg.append(':');
        encodeLogField(msg, Util.decodeString(adm.adminId));
      }
      msg.append('"');
    }
    
    // log the referrer
    if(logReferrer) {
      msg.append(" \"");
      encodeLogField(msg, referrer);
      msg.append('"');
    }
    
    msg.append('\n');

    try {
      log.write(msg.toString());
      if(flushCounter++ > 1000 || lastFlushTime+3000<System.currentTimeMillis()) {
        log.flush();
        lastFlushTime = System.currentTimeMillis();
      }
    } catch (IOException e){
      System.err.println("Error writing to log: "+e); 
    }
  }
  

  private static final void encodeLogField(StringBuffer sb, String str) {
    if(str==null) return;
    int strLen = str.length();
    char ch;
    for(int i=0; i<strLen; i++) {
      ch = str.charAt(i);
      if(ch=='\\')
        sb.append("\\\\");
      else
        sb.append(ch);
    }
  }

  public void finalize()
    throws Throwable
  {
    System.err.println("closing access log");
    try {
      if(log!=null) log.close();
      log = null;
    } catch (Throwable t) {
      t.printStackTrace(System.err);
    }
    
    super.finalize();
  }
  
}
